package cs.ubbcluj.ro.algorithm;

import cs.ubbcluj.ro.datatypes.interfaces.Matrix;
import cs.ubbcluj.ro.datatypes.interfaces.Vector;
import cs.ubbcluj.ro.linearsystem.implementations.LinearSystemLU;
import cs.ubbcluj.ro.lp.LP;

public class AlgorithmPredictorCorrector extends AlgorithmBase {

	Vector deltaX_affin;
	Vector deltaY_affin;
    Vector deltaS_affin;
	double alphaPrimal_affin;
	double alphaDual_affin;
	double mu_affin;

	public AlgorithmPredictorCorrector(LP lp, double sigma, double rho,
			double epsilon, double theta) {
		super(lp, sigma, rho, epsilon, theta);	
	}
	
	public void solveProblem() throws InterruptedException{

		int iterationCount=0;
		
		do{
			
			/******************************************************************************
			 * affin-scaling
			 ******************************************************************************
			 */
			
			// get value of mu
			mu = getAffinMu();
			
			// get value of nu
			nu = calculateAffinRightHandSide(false);
			
			// calculate the value of rb		
			rb = lp.getRb();
			
			// calculate the value of rc
			rc = lp.getRc();
			
			// calculate AXS^-1
			Matrix AXS_1 = lp.getAXS_1();

			// calculate AS^-1
			Matrix AS_1 = lp.getAS_1(); //helperMatrix1
			
			// calculate deltaY_affin
			Matrix AD2At = lp.getAD2At();			
			deltaY = getDeltaY(AD2At, AXS_1, AS_1);
			deltaY_affin=deltaY;
			
			// calculate deltaS_affin
			deltaS = getDeltaS();
			deltaS_affin=deltaS;
			
			// calculates deltaX_affin
			deltaX = getDeltaX();
			deltaX_affin=deltaX;
			
			// calculates alphaPrimal_affin
			alphaPrimal = min(lp.getX(), deltaX);
			
			// calculates alphaDual_affin
			alphaDual = min(lp.getS(), deltaS);
			
			// set value of mu_affin
			setMuAffin();
			
			
			/*******************************************************************************
			 * centering-corrector
			 *******************************************************************************
			 */
			
			// calculates sigma
			sigma=mu_affin/mu;
			sigma=sigma*sigma*sigma;
			
			// calculates mu_corrector
			mu*=sigma;
			
			// calculates nu_corrector
			setRightHandSide();
			
			// calculates deltaY_corrector
			deltaY=getDeltaYCorrector(AD2At, AXS_1, AS_1);
			
			// calculate deltaS_corrector
			deltaS = getDeltaSCorrector();
			
			// calculates deltaX_corrector
			deltaX = getDeltaX();
			
			deltaX=deltaX.sum(deltaX_affin);
			
			deltaY=deltaY.sum(deltaY_affin);
			
			deltaS=deltaS.sum(deltaS_affin);
			
			// calculates alphaPrimal_corrector
			alphaPrimal = min(lp.getX(), deltaX);
			
			// calculates alphaDual_corrector
			alphaDual = min(lp.getS(), deltaS);
		
			// set values for x, y, s
			setX();
			setY();
			setS();
			
			iterationCount++;

			System.out.println("----------------------------");
			System.out.println("Primal = " + lp.getPrimal());
			System.out.println("Dual= " + lp.getDual());
			System.out.println("iteration = " + iterationCount);
			
			
		}while (!isResultGoodEnough() && iterationCount < 50);
		
		System.out.println("\niterationCount = " + iterationCount);
	}

	
	/**
	 * calculates the value of variable 'mu'
	 * formula: mu = x^T * s / n
	 * @return double
	 * @throws InterruptedException 
	 */
	public double getAffinMu() throws InterruptedException {	
		Vector resultVector = lp.getX().createHelperVector(lp.getXLength());
		resultVector.set(lp.getX());
		return resultVector.product(lp.getS()) / lp.getXLength();		
	}
	
	/**
	 * calculates the value of 'nu' given by the formula: nu = - x * s
	 * abstract function which will be overwritten by extended classes
	 * @return Vector
	 * @throws InterruptedException
	 */
	public Vector calculateAffinRightHandSide(boolean useSuperClass) throws InterruptedException {
		Vector resultVector = lp.getX().createHelperVector(lp.getXLength());
		resultVector.set(lp.getX());
		resultVector.productScalar(lp.getS());
		resultVector.product(-1);
		return resultVector;
	}
	/**
	 *sets values for muAffin given by the formula mu_affin=((x + alphaPrimal_affin * deltaX_affin).transpose *
	 *								(s + alphaDual_affin * deltaS_affin)) / x.length; (n)
	 * @throws InterruptedException 
	 */
	private void setMuAffin() throws InterruptedException {
		double sum=0;
		Vector dX = deltaX.createHelperVector(deltaX.getNrOfNonZeros());
		Vector dS = deltaS.createHelperVector(deltaS.getNrOfNonZeros());
		Vector x = lp.getX().createHelperVector(lp.getX().getNrOfNonZeros());
		Vector s= lp.getS().createHelperVector(lp.getS().getNrOfNonZeros());
		
		dX.set(deltaX);
		dS.set(deltaS);
		x.set(lp.getX());
		s.set(lp.getS());
		sum = (x.sum(dX.product(alphaPrimal))).product(s.sum(dS.product(alphaDual)));
		mu_affin=sum/lp.getXLength();
		
	}
	/**
	 * nu_corrector = mu_corrector * e 
			//		- delta_x_affin * delta_s_affin
	 * @throws InterruptedException 
	 */
	private void setRightHandSide() throws InterruptedException {
		int szam=nu.getNrOfNonZeros();
		for (int i=0;i<szam;i++){
			nu.set(i,mu-deltaX.get(i)*deltaS.get(i));
		}
	}
	
	/**
	 * calculates deltaY_corrector using the formula: AD2At * deltaY = - (AS^-1 * nu + AXS^-1 )
	 * @return value of deltaY
	 * @throws InterruptedException 
	 */
	public Vector getDeltaYCorrector(Matrix AD2At, Matrix AXS_1, Matrix AS_1) throws InterruptedException {		
		// add linear system solving
		Vector b = AS_1.product(nu);		
		LinearSystemLU linearSystem = new LinearSystemLU(AD2At, b);		
		return linearSystem.solveSystem().product(-1);
	}
	
	/**
	 * calculates deltaS_corrector using the formula: deltaS = -A^T * deltaY
	 * @return value of deltaS
	 * @throws InterruptedException
	 */
	private Vector getDeltaSCorrector() throws InterruptedException {
		return lp.getA().trans().product(-1).product(deltaY);
	}

}
